<?php

namespace app\backend\services\auth;

/**
 * 规则
 *
 * @property string       $filter
 * @property string       $name
 * @property string       $route 唯一
 * @property string|array $method
 *
 * @package auth
 * Author: zsw iszsw@qq.com
 */
class Permission
{

    /**
     * 所有人访问
     */
    const ALLOW_ALL_FILTER = '*';

    /**
     * 登录访问
     */
    const ALLOW_LOGIN_FILTER = '@';

    /**
     * 授权访问
     */
    const ALLOW_AUTHORIZED_FILTER = '&';

    /**
     * @var array
     */
    protected $children = [];

    /**
     * @var self|null
     */
    protected $parent;

    /**
     * @var array
     */
    protected $data = [];

    public function __construct( $permission )
    {
        $this->verification($permission);
    }

    /**
     * 初始化验证
     *
     * @param $permission
     */
    protected function verification($permission)
    {
        if (is_string($permission)) {
            $permission = ['route' => $permission];
        }
        $this->data = array_merge(
            [
                'filter' => self::ALLOW_AUTHORIZED_FILTER,
                'name'   => '',
                'route'  => '#',
                'parent' => null,
                'method' => ['*'],
            ], $permission
        );

        foreach ($this->data as $k => $v) {
            $method = strtolower($k) . 'Format';
            if (method_exists($this, $method)) {
                $this->data[$k] = $this->$method($v);
            }
        }
    }

    public function setParent(self $permission): self
    {
        $this->parent = $permission;
        return $this;
    }

    /**
     * @param self|array<self> $permission
     */
    public function addChildren($permission)
    {
        if ($permission instanceof self)
        {
            $this->children[] = $permission;
        } else
        {
            foreach ($permission as $p)
            {
                $this->addChildren($p);
            }
        }
    }

    /**
     * 路由格式化
     *
     * @param $route
     *
     * @return string
     */
    private function routeFormat($route){
        return trim($route, '/');
    }

    /**
     * 请求类型格式化
     *
     * @param $method
     *
     * @return array
     */
    private function methodFormat($method){
        $method = (array)$method;
        foreach ($method as &$m) {
            $m = strtolower($m);
        }
        return $method;
    }

    public function getChildren()
    {
        return $this->children;
    }

    public function getParent()
    {
        return $this->parent;
    }

    /**
     * 获取最高级
     *
     * @return $this
     */
    public function getAncestor()
    {
        return is_null($this->getParent()) ? $this : $this->parent->getAncestor();
    }

    public function __get($name)
    {
        return $this->data[$name] ?? null;
    }

    public function getData()
    {
        return $this->data;
    }

    private function methodFilter($method)
    {
        $method = strtolower($method);
        return in_array($method, $this->method) || in_array('*', $this->method);
    }

    /**
     * @param string|array $rule 校验规则 route|['route'=>`route`]
     * @param Authority $authority
     *
     * @return bool
     */
    public function check($rule, Authority $authority): bool
    {
        $rules = is_array($rule) ? $rule : [];
        unset($rules['route']);
        foreach ($rules as $k => $r)
        {
            $method = strtolower($k).'Filter';
            if ( !(method_exists($this, $method) ? $this->$method($r) : $this->$k == $r) ){
                return false;
            }
        }

        return  $authority->isAdministrator()
            || $this->allowAllFilter()
            || $this->allowLoginFilter($authority)
            || $this->allowAuthorizedFilter($authority);
    }

    /**
     * 公共访问
     *
     * @return bool
     */
    private function allowAllFilter(): bool
    {
        return $this->filter === self::ALLOW_ALL_FILTER;
    }

    /**
     * 登录访问
     *
     * @param Authority $authority
     *
     * @return bool
     */
    private function allowLoginFilter(Authority $authority): bool
    {
        return $this->filter === self::ALLOW_LOGIN_FILTER && !$authority->guest();
    }

    /**
     * 授权访问
     *
     * @param Authority $authority
     *
     * @return bool
     */
    private function allowAuthorizedFilter(Authority $authority): bool
    {
        return $authority->isAuthorized( $this );
    }


}
